package org.ubc.cs410.disposal2020;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.jdo.PersistenceManager;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Game {
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;

	@Persistent(defaultFetchGroup="true",mappedBy = "game")
	private List<Player> players;

	// indicates whether this is an ongoing game or finished one
	@Persistent
	private boolean inSession;

	// slug used in URL for the game
	@Persistent
	private String slug;
	@Persistent
	private Long mapId;
	@Persistent(defaultFetchGroup="true", serialized = "true")
	private Set<Garbage> garbageState;
	@Persistent
	private int turn;
	@Persistent
	private int maxTurn;

	// Default maximum amount of turns to end game
	private static final int DEFAULT_MAX_TURN = 10;
	private static final int MAX_GARBAGE = 3000;
	private static final int MIN_GARBAGE = 150;
	private static final int DEFAULT_GARBAGE = 2100;

	/*
	 * Game Class that stores player information, map and garbage states in the game
	 * 
	 */
	public Game(Map map, List<Player> players, int maxTurn, int totalGarbage) {
		this.players = players;
		this.mapId = map.getId();
		this.inSession = false;
		// TODO: Better slugs.
		this.slug = java.util.UUID.randomUUID().toString();
		this.turn = 0;		
		this.maxTurn = maxTurn;
		this.garbageState = new HashSet<Garbage>();

		if (totalGarbage>MAX_GARBAGE || totalGarbage<MIN_GARBAGE) {
			//totalGarbage = map.getTotalGarbage();
			totalGarbage = DEFAULT_GARBAGE;
		}

		Map m = getMap();
		Set<BuildingBoundary> buildings = m.getBuildings();
		Random r = new Random();
		double averageAmount = (double)totalGarbage/(double)buildings.size()*3;
		// Randomly distribute the garbage over 1/3 of the buildings.
		for (BuildingBoundary b : buildings) {
			if (r.nextInt(100)>66) {
				int amount = (int) Math.ceil((double)averageAmount*((double)r.nextInt(200)/100));
				garbageState.add(Garbage.generateGarbage(b, amount));
			}
		}		
	}

	public Map getMap() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		return pm.getObjectById(Map.class, mapId);
	}

	public Game(Map map, List<Player> players, int totalGarbage) {
		this(map, players, DEFAULT_MAX_TURN, totalGarbage);
	}

	public Game(Map map, List<Player> players) {
		this(map, players, DEFAULT_MAX_TURN, DEFAULT_GARBAGE);
	}

	public int getTurn() {
		return turn;
	}

	public boolean nextTurn() {	  
		if (turn==maxTurn) {
			return false;
		}

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Game g = pm.getObjectById(Game.class, id);
			for (Player p: g.getPlayers()) {
				for (Robot r: p.getRobots()) {
					r.makeMovable();
				}
			}  	  
			g.incrTurn();
		} finally {
			pm.close();
		}
		return true;
	}

	private void incrTurn() {
		this.turn++;    
	}

	// Adapted from previous method in GameServiceImpl.
	public List<Robot> getRobots() {
		List<Robot> robots = new ArrayList<Robot>();
		for (Player player : this.players)
			robots.addAll(player.getRobots());
		return robots;
	}

	public void addPlayer(Player player) {
		this.players.add(player);
	}

	public String getSlug() {
		return slug;
	}

	public List<Player> getPlayers() {
		return players;
	}



	public long getId() {
		return this.id;
	}

	public Long getMapId() {
		return this.mapId;
	}

	public boolean isActive() {
		return this.inSession;
	}

	public boolean isWaiting() {
		return (!this.inSession && this.players.size()==1);
	}

	public void start() {
		this.turn = 1;
		this.inSession = true;    
	}

	public void checkTurn() {
		for (Player p: this.getPlayers()) {
			for (Robot r: p.getRobots()) {
				if (r.isMovable() == true)
					return;
			}
		}

		for (Player p: this.getPlayers()) {
			for (Robot r: p.getRobots()) {
				r.makeMovable();
			}
		}

		turn++;
	}

	public void setTurn() {
		turn++;
		return;
	}

	public Set<Garbage> getGarbage() {
		return this.garbageState;
	}

	public void removeGarbage(Set<Garbage> removeThese) {
		garbageState.removeAll(removeThese);
	}

	public int collectGarbage(double lat, double lng, double radius) {
		Location loc = new Location(lat,lng);
		int collected = 0;
		Set<Garbage> toBeRemoved = new HashSet<Garbage>();
		System.out.println(garbageState.size());
		for (Garbage g : getGarbage()) {
			if (Distance.calculate(loc, g)<radius) {
				collected += g.getAmount();
				toBeRemoved.add(g);
			}
		}
		removeGarbage(toBeRemoved);
		System.out.println(garbageState.size());
		System.out.println(collected);
		return collected;
	}

	public void makeInactive() {
		this.inSession = false;
	}

	public void end() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Game g = pm.getObjectById(Game.class, id);
			for (Player p: g.getPlayers()) {
				p.retire();
			}     
			g.makeInactive();
		} finally {
			pm.close();
		}    
	}

	public boolean isOver() {
		return (!this.inSession && this.players.size()==2);
	}
}
